<!DOCTYPE html>
<html>

<head>
  <meta charset='utf-8'>
  <title>鼠标控制物体旋转</title>
  <link rel="shortcut icon" href="../icon/sparrow.png" />
  <link rel="bookmark" href="../icon/sparrow.png" type="image/x-icon" />
  <link rel="stylesheet" href="../style/common.css" />
  <!-- 对于制作原型或学习，你可以这样使用最新版本： -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script src="../lib/webgl-debug.js"></script>
  <script src="../lib/webgl-utils.js"></script>
  <script src="../lib/cuon-matrix.js"></script>
  <script src="../lib/cuon-utils.js"></script>
  <script src="../lib/allx-utils.js"></script>
</head>

<body>
  <div id='gl33'>
    <h2>gl33 多节点模型</h2>
    <canvas id="c33" width="400" height="400">
      您的浏览器不支持canvas
    </canvas>
    <script type="text/javascript">
      // 顶点着色器程序2
      let VSHADER_SOURCE33 = `
        attribute vec4 a_Position; // 顶点坐标
        attribute vec4 a_Color; // 顶点颜色
        attribute vec4 a_Normal; // 法向量

        uniform mat4 u_MvpMatrix; // 模型视图投影矩阵
        uniform mat4 u_ModelMatrix; // 模型矩阵
        uniform mat4 u_NormalMatrix; // 用来变换法向量的矩阵
        // uniform vec3 u_LightDirection; // 光线归一化方向（平行光使用）

        varying vec4 v_Color; // 向片元着色器传递颜色
        varying vec3 v_Normal;
        varying vec3 v_Position;

        void main(){
          gl_Position = u_MvpMatrix * a_Position;

          v_Position = vec3(u_ModelMatrix * a_Position); // 计算顶点的世界坐标
          // v_Normal = normalize(vec3(a_Normal)); // 归一化法向量
          v_Normal = normalize(vec3(u_NormalMatrix * a_Normal)); // 计算变换后的法向量并归一化法向量
          v_Color = a_Color;
        }`;

      // 片元着色器程序2
      let FSHADER_SOURCE33 = `
        precision mediump float; // 声明片元着色器float的精度（必须指定）

        uniform vec3 u_LightColor; // 光线颜色
        uniform vec3 u_LightPosition; // 光源位置（世界坐标系）
        uniform vec3 u_AmbientLight; // 环境光颜色

        varying vec4 v_Color; // 接收顶点着色器传递的颜色
        varying vec3 v_Normal; 
        varying vec3 v_Position;

        void main(){
          vec3 normal = normalize(v_Normal); // 对插值后的法向量进行归一化（内插后长度不一定为1.0）
          vec3 lightDirection = normalize(u_LightPosition - v_Position); // 计算光线方向并归一化
          float nDotL = max(dot(lightDirection, normal), 0.0); // 计算光线方向和法向量的点积

          vec3 diffuse = u_LightColor * vec3(v_Color) * nDotL; // 计算漫反射光的颜色
          vec3 ambient = u_AmbientLight * v_Color.rgb; // 计算环境反射光颜色

          gl_FragColor = vec4(diffuse + ambient, v_Color.a); // 漫反射光 + 环境反射光
          // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 漫反射光 + 环境反射光
        }`;


      // 获取canvas，获取webGL绘图上下文
      const gl33 = document.querySelector("#c33").getContext("webgl");
      if (!gl33) console.log(Error("无法获得webgl的绘图上下文"));

      // 初始化着色器
      if (!initShaders(gl33, VSHADER_SOURCE33, FSHADER_SOURCE33)) console.log(Error("无法初始化着色器"));

      // 设置顶点信息（传入attribute变量），返回绘制次数
      let n33 = initVertexBuffers33(gl33);
      if (n33 < 0) console.log(Error("无法设置顶点信息（传入attribute变量）"));

      gl33.enable(gl33.DEPTH_TEST); // 开启深度检测（隐藏面消除）
      gl33.clearColor(0.0, 0.0, 0.0, 1.0); // 设置背景色

      // 获取 uniform 变量位置
      let u_mvpMatrix33 = gl33.getUniformLocation(gl33.program, "u_MvpMatrix");
      let u_modelMatrix33 = gl33.getUniformLocation(gl33.program, "u_ModelMatrix");
      let u_normalMatrix33 = gl33.getUniformLocation(gl33.program, "u_NormalMatrix");
      let u_lightColor33 = gl33.getUniformLocation(gl33.program, "u_LightColor");
      let u_ambientLight33 = gl33.getUniformLocation(gl33.program, "u_AmbientLight");
      let u_lightPosition33 = gl33.getUniformLocation(gl33.program, "u_LightPosition");

      let ANGLE_STEP33 = 3.0; // 每次按键转动的角度
      let g_arm1Angle33 = 90.0; // arm1当前的角度
      let g_joint1Angle33 = 0.0; // joint1的当前角度，即arm2的角度
      let g_joint2Angle33 = 0.0; //joint2的当前角度
      let g_joint3Angle33 = 0.0; //joint3的当前角度

      let g_modelMatrix33 = new Matrix4(); // 全局 模型矩阵
      let g_mvpMatrix33 = new Matrix4(); // 全局 模型视图投影矩阵
      let g_normalMatrix33 = new Matrix4(); // 全局 魔法矩阵

      let g_matrixStack33 = []; //存储矩阵的值
      let pushMatrix33 = (m) => g_matrixStack33.push(new Matrix4(m)); //将矩阵压入栈
      let popMatrix33 = () => g_matrixStack33.pop(); //从栈中弹出矩阵

      gl33.uniform3f(u_lightColor33, 1.0, 1.0, 1.0); // 传入 白色光线
      gl33.uniform3f(u_ambientLight33, 0.1, 0.1, 0.1); // 传入 环境光颜色 更真实
      gl33.uniform3f(u_lightPosition33, 20.0, 20.0, 40.0); // 传入 点光源位置（世界坐标）

      let viewProjMatrix33 = new Matrix4(); // 创建视图投影矩阵
      viewProjMatrix33.setPerspective(50.0, 1, 1.0, 100.0); // 垂直视角、近裁剪面宽高比、近裁剪面位置、远裁剪面位置
      viewProjMatrix33.lookAt(10.0, 10.0, 30.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // 视点、视线（被观察对象位置）、上方向

      //绘制机器臂
      draw33(gl33, n33, viewProjMatrix33, u_mvpMatrix33, u_modelMatrix33, u_normalMatrix33);

      //添加键盘按键交互事件
      document.onkeydown = function (e) {
        handleKeyDown33(e, gl33, n33, viewProjMatrix33, u_mvpMatrix33, u_modelMatrix33, u_normalMatrix33);
      };

      function handleKeyDown33(event, gl, n, viewProjMatrix, u_mvpMatrix, u_modelMatrix, u_normalMatrix) {
        switch (event.keyCode) {
          case 87: // W键 -> 以joint1为中心沿着z轴旋转（增量）
            if (g_joint1Angle33 < 135.0) g_joint1Angle33 += ANGLE_STEP33;
            break;
          case 83: // S键 -> 以joint1为中心沿着z轴旋转（减量）
            if (g_joint1Angle33 > -135.0) g_joint1Angle33 -= ANGLE_STEP33;
            break;
          case 68: // D键 -> 以y轴进行水平旋转（增量）
            g_arm1Angle33 = (g_arm1Angle33 + ANGLE_STEP33) % 360;
            break;
          case 65: // A键 -> 以y轴进行水平旋转（减量）
            g_arm1Angle33 = (g_arm1Angle33 - ANGLE_STEP33) % 360;
            break;
          case 90: // Z键 -> 使joint2正向转动
            g_joint2Angle33 = (g_joint2Angle33 + ANGLE_STEP33) % 360;
            break;
          case 88: // X键 -> 使joint2负向转动
            g_joint2Angle33 = (g_joint2Angle33 - ANGLE_STEP33) % 360;
            break;
          case 86: // V键 -> 使joint3正向转动
            if (g_joint3Angle33 < 60.0) g_joint3Angle33 = (g_joint3Angle33 + ANGLE_STEP33) % 360;
            break;
          case 67: // C键 -> 使joint3负向转动
            if (g_joint3Angle33 > -60.0) g_joint3Angle33 = (g_joint3Angle33 - ANGLE_STEP33) % 360;
            break;
          default:
            return; // 其他按键没作用
        }

        draw33(gl, n, viewProjMatrix, u_mvpMatrix, u_modelMatrix, u_normalMatrix);
      }

      function initVertexBuffers33(gl) {
        var vertices = new Float32Array([ // Vertex coordinates（长方体3宽度，高度10，长度3，其原点在其底部）
          0.5, 1.0, 0.5, -0.5, 1.0, 0.5, -0.5, 0.0, 0.5, 0.5, 0.0, 0.5, // v0-v1-v2-v3 front
          0.5, 1.0, 0.5, 0.5, 0.0, 0.5, 0.5, 0.0, -0.5, 0.5, 1.0, -0.5, // v0-v3-v4-v5 right
          0.5, 1.0, 0.5, 0.5, 1.0, -0.5, -0.5, 1.0, -0.5, -0.5, 1.0, 0.5, // v0-v5-v6-v1 up
          -0.5, 1.0, 0.5, -0.5, 1.0, -0.5, -0.5, 0.0, -0.5, -0.5, 0.0, 0.5, // v1-v6-v7-v2 left
          -0.5, 0.0, -0.5, 0.5, 0.0, -0.5, 0.5, 0.0, 0.5, -0.5, 0.0, 0.5, // v7-v4-v3-v2 down
          0.5, 0.0, -0.5, -0.5, 0.0, -0.5, -0.5, 1.0, -0.5, 0.5, 1.0, -0.5 // v4-v7-v6-v5 back
        ]);

        var colors = new Float32Array([ // Colors
          1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v1-v2-v3 front
          1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v3-v4-v5 right
          1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v5-v6-v1 up
          1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v1-v6-v7-v2 left
          1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v7-v4-v3-v2 down
          1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 // v4-v7-v6-v5 back
        ]);

        var normals = new Float32Array([ // 每一个面的法向量
          0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, // v0-v1-v2-v3 front
          1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, // v0-v3-v4-v5 right
          0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v5-v6-v1 up
          -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, // v1-v6-v7-v2 left
          0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, // v7-v4-v3-v2 down
          0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0 // v4-v7-v6-v5 back
        ]);

        var indices = new Uint8Array([ // 索引
          0, 1, 2, 0, 2, 3, // front
          4, 5, 6, 4, 6, 7, // right
          8, 9, 10, 8, 10, 11, // up
          12, 13, 14, 12, 14, 15, // left
          16, 17, 18, 16, 18, 19, // down
          20, 21, 22, 20, 22, 23 // back
        ]);

        const FSIZE = vertices.BYTES_PER_ELEMENT;

        if (!initArrayBuffer(gl, "a_Position", vertices, 3, gl.FLOAT)) return -1; // 创建缓冲区并赋值attribute
        if (!initArrayBuffer(gl, "a_Color", colors, 3, gl.FLOAT)) return -1; // 创建缓冲区并赋值attribute
        if (!initArrayBuffer(gl, "a_Normal", normals, 3, gl.FLOAT)) return -1; // 创建缓冲区并赋值attribute

        let indexBuffer = gl.createBuffer();
        if (!indexBuffer) {
          console.log(Error('Failed to create index buffer'));
          return -1;
        }
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); // 写ELEMENT_ARRAY_BUFFER入缓冲区

        return indices.length;
      }

      //绘制图形
      function draw33(gl, n, viewProjMatrix, u_mvpMatrix, u_modelMatrix, u_normalMatrix) {
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // 清空颜色及深度缓冲区

        //绘制基座
        let baseHeight = 2.0; //基座的高度
        g_modelMatrix33.setTranslate(0.0, -12.0, 0.0); //设置为平移矩阵，并沿y轴向下移动12
        drawBox33(gl, n, 10.0, baseHeight, 10.0, viewProjMatrix, u_mvpMatrix, u_modelMatrix, u_normalMatrix);

        //arm1 第一节胳膊
        var arm1Length = 10.0; //第一节胳膊的长度
        g_modelMatrix33.translate(0.0, baseHeight, 0.0); //移至基座
        g_modelMatrix33.rotate(g_arm1Angle33, 0.0, 1.0, 0.0); //沿y轴旋转
        drawBox33(gl, n, 4.0, arm1Length, 4.0, viewProjMatrix, u_mvpMatrix, u_modelMatrix, u_normalMatrix);

        //arm2 第二节胳膊
        var arm2Length = 8.0; //
        g_modelMatrix33.translate(0.0, arm1Length, 0.0); //移至joint1
        g_modelMatrix33.rotate(g_joint1Angle33, 0.0, 0.0, 1.0); //沿z轴旋转
        drawBox33(gl, n, 3.0, arm2Length, 3.0, viewProjMatrix, u_mvpMatrix, u_modelMatrix, u_normalMatrix);

        //机器人手掌的制作
        var palmLength = 4.0;
        g_modelMatrix33.translate(0.0, arm2Length, 0.0); //移至joint2
        g_modelMatrix33.rotate(g_joint2Angle33, 0.0, 1.0, 0.0); //沿y轴旋转
        drawBox33(gl, n, 2.0, palmLength, 6.0, viewProjMatrix, u_mvpMatrix, u_modelMatrix, u_normalMatrix);

        //移至手掌一端的中点
        g_modelMatrix33.translate(0.0, palmLength, 0.0);

        //绘制finger1 第一个手指头
        pushMatrix33(g_modelMatrix33); //将当前的g_modelMatrix的值保存
        g_modelMatrix33.translate(0.0, 0.0, 2.0); //沿z轴移动
        g_modelMatrix33.rotate(g_joint3Angle33, 1.0, 0.0, 0.0); //沿x轴旋转
        drawBox33(gl, n, 1.0, 3.0, 1.0, viewProjMatrix, u_mvpMatrix, u_modelMatrix, u_normalMatrix);
        g_modelMatrix33 = popMatrix33(); //获取到压入时的g_modelMatrix矩阵的值

        //绘制finger2 第二个手指头
        g_modelMatrix33.translate(0.0, 0.0, -2.0); //沿z轴负方向移动
        g_modelMatrix33.rotate(-g_joint3Angle33, 1.0, 0.0, 0.0); //沿x轴旋转
        drawBox33(gl, n, 1.0, 3.0, 1.0, viewProjMatrix, u_mvpMatrix, u_modelMatrix, u_normalMatrix);
      }

      //绘制立方体
      function drawBox33(gl, n, width, height, depth, viewProjMatrix, u_mvpMatrix, u_modelMatrix, u_normalMatrix) {
        pushMatrix33(g_modelMatrix33); // 入栈

        g_modelMatrix33.scale(width, height, depth);
        gl.uniformMatrix4fv(u_modelMatrix, false, g_modelMatrix33.elements); // 模型矩阵

        g_mvpMatrix33.set(viewProjMatrix);
        g_mvpMatrix33.multiply(g_modelMatrix33);
        gl.uniformMatrix4fv(u_mvpMatrix, false, g_mvpMatrix33.elements); // 模型视图投影矩阵

        g_normalMatrix33.setInverseOf(g_modelMatrix33);
        g_normalMatrix33.transpose();
        gl.uniformMatrix4fv(u_normalMatrix, false, g_normalMatrix33.elements);

        //绘制
        gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);

        g_modelMatrix33 = popMatrix33(); // 出栈
      }
    </script>
  </div>
</body>

</html>